/*****************************************************************************
* FILE: join.c
* DESCRIPTION:
*   This example demonstrates how to "wait" for thread completions by using
*   the Pthread join routine.  Threads are explicitly created in a joinable
*   state for portability reasons. Use of the pthread_exit status argument is 
*   also shown. Compare to detached.c
* AUTHOR: 8/98 Blaise Barney
* LAST REVISED:  01/30/09
******************************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <linux/netfilter/xt_state.h>
#include <linux/netfilter/nf_conntrack_common.h>
#include <libiptc/libiptc.h>

#define NUM_THREADS	2

#define __DEBUG printf

#define IPT_ALIGN       XT_ALIGN
#define IPT_STATE_BIT   XT_STATE_BIT

#define IPTABLES_FILTER "filter"
#define IPT_MATCH_STATE "state"
#define IPTABLES_FILTER_FORWARD "FORWARD"
#define IPTC_SUCCESS    1

pthread_mutex_t mutexsum;

enum {
        CMD_PE_ERR_INTERNAL = 0,

};

int add_policy( void )
{
        int ret = -1;
        int tsize,esize,msize;
        struct ipt_entry_target *target;
        struct ipt_entry_match *match;
        struct xt_state_info *state;
        struct ipt_entry *e;


        //struct iptc_handle *handle;
        struct xtc_handle *handle;

        handle=iptc_init(IPTABLES_FILTER);

        if(handle == NULL){
                ret=CMD_PE_ERR_INTERNAL;
                __DEBUG(" Can't initialize iptc handle!\n");
                goto out;
        }

        /* prepare the standard ACCEPT target */
        tsize=IPT_ALIGN(sizeof(struct ipt_entry_target))+IPT_ALIGN(sizeof(int));
        msize=IPT_ALIGN(sizeof(struct ipt_entry_match)) + IPT_ALIGN(sizeof(struct xt_state_info));

        esize=IPT_ALIGN(sizeof(struct ipt_entry))+msize+tsize;
        e=(struct ipt_entry*)malloc(esize);

        if(e == NULL){
                ret=CMD_PE_ERR_INTERNAL;
                __DEBUG(" Can't alloc memory from system!\n" );
                goto out;
        }
        memset(e,0,esize);
        e->target_offset=esize - tsize;
        e->next_offset=esize;

        /* prepare the state match */
        match=(struct ipt_entry_match *)e->elems;

        strcpy(match->u.user.name,IPT_MATCH_STATE);
        match->u.user.match_size = msize;

        state=(struct xt_state_info *)match->data;
        state->statemask |= IPT_STATE_BIT (IP_CT_ESTABLISHED) | IPT_STATE_BIT (IP_CT_RELATED);


        /* prepare the standard ACCEPT target */
        target=(struct ipt_entry_target *)((char*)e + e->target_offset);
        strcpy(target->u.user.name,IPTC_LABEL_ACCEPT);
        target->u.user.target_size = tsize;


        /* add it to the kernel */
        if(iptc_append_entry(IPTABLES_FILTER_FORWARD,e,handle) != IPTC_SUCCESS)
        {
                ret=CMD_PE_ERR_INTERNAL;
                __DEBUG(" Can't insert rule into kernel!\n" );
                goto out;
        }
        if(iptc_commit(handle) != IPTC_SUCCESS)
        {
                ret=CMD_PE_ERR_INTERNAL;
                __DEBUG(" Can't commit changes!" );
                goto out;
        }

	if(e)
		free(e);

out:
	return ret;
}


void *BusyWork(void *t)
{

	long tid;
	int i = 0;
	tid = (long)t;
	printf("Thread %ld starting...\n",tid);
	for (i=0; i<500; i++) {
		pthread_mutex_lock (&mutexsum);
		add_policy();
		pthread_mutex_unlock (&mutexsum);
	}
	pthread_exit((void*) t);
}

int main (int argc, char *argv[])
{
	pthread_t thread[NUM_THREADS];
	pthread_attr_t attr;
	int rc;
	long t;
	void *status;

	clock_t start, finish;
	double duration;

	start = clock();
	/* pthread lock */
	pthread_mutex_init(&mutexsum, NULL);	
	/* Initialize and set thread detached attribute */
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

	for(t=0; t<NUM_THREADS; t++) {
		printf("Main: creating thread %ld\n", t);
		rc = pthread_create(&thread[t], &attr, BusyWork, (void *)t); 
		if (rc) {
			printf("ERROR; return code from pthread_create() is %d\n", rc);
			exit(-1);
		}
	}

	/* Free attribute and wait for the other threads */
	pthread_attr_destroy(&attr);
	for(t=0; t<NUM_THREADS; t++) {
		rc = pthread_join(thread[t], &status);
		if (rc) {
			printf("ERROR; return code from pthread_join() is %d\n", rc);
			exit(-1);
		}
		printf("Main: completed join with thread %ld having a status of %ld\n",t,(long)status);
	}

	printf("Main: program completed. Exiting.\n");
	pthread_mutex_destroy(&mutexsum);
	finish = clock();
	duration = (double)(finish - start) / CLOCKS_PER_SEC;
	printf( "%f seconds\n", duration );


	pthread_exit(NULL);
}

